package com.bestvike.stone.spring.quartz;

import com.bestvike.stone.core.collection.SetUtils;
import org.quartz.CronScheduleBuilder;
import org.quartz.Job;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.quartz.core.QuartzScheduler;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

/**
 * Created by 许崇雷 on 2018-08-27.
 */
@Component
@ConditionalOnClass(QuartzScheduler.class)
public final class QuartzUtils {
    private static Scheduler scheduler;

    QuartzUtils(SchedulerFactoryBean schedulerFactoryBean) {
        scheduler = schedulerFactoryBean.getScheduler();
    }

    /**
     * 保存任务和触发器并进入可调度状态
     *
     * @param jobName          任务名称
     * @param jobGroupName     任务组名称
     * @param jobClass         任务类
     * @param triggerName      触发器名称
     * @param triggerGroupName 触发器组名称
     * @param cronExpression   cron 表达式
     * @throws SchedulerException 操作失败抛出异常
     */
    public static void scheduleJob(String jobName, String jobGroupName, Class<? extends Job> jobClass, String triggerName, String triggerGroupName, String cronExpression) throws SchedulerException {
        Assert.hasLength(jobName, "任务名称");
        Assert.hasLength(jobGroupName, "任务组名称");
        Assert.notNull(jobClass, "任务类");
        Assert.hasLength(triggerName, "触发器名称");
        Assert.hasLength(triggerGroupName, "触发器组名称");
        Assert.hasLength(cronExpression, "cron 表达式");

        JobDetail jobDetail = JobBuilder.newJob(jobClass)
                .withIdentity(jobName, jobGroupName)
                .build();
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerName, triggerGroupName)
                .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                .build();
        scheduler.scheduleJob(jobDetail, SetUtils.singleton(trigger), true);
    }

    /**
     * 删除任务以及其所有的触发器
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务组名称
     * @throws SchedulerException 操作失败抛出异常
     */
    public static void deleteJob(String jobName, String jobGroupName) throws SchedulerException {
        Assert.hasLength(jobName, "任务名称");
        Assert.hasLength(jobGroupName, "任务组名称");

        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        scheduler.deleteJob(jobKey);
    }

    /**
     * 挂起任务以及其所有的触发器,暂停期间任务将堆积
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务组名称
     * @throws SchedulerException 操作失败抛出异常
     */
    public static void pauseJob(String jobName, String jobGroupName) throws SchedulerException {
        Assert.hasLength(jobName, "任务名称");
        Assert.hasLength(jobGroupName, "任务组名称");

        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复任务以及其所有的触发器,暂停期间堆积的任务将立即执行
     *
     * @param jobName      任务名称
     * @param jobGroupName 任务组名称
     * @throws SchedulerException 操作失败抛出异常
     */
    public static void resumeJob(String jobName, String jobGroupName) throws SchedulerException {
        Assert.hasLength(jobName, "任务名称");
        Assert.hasLength(jobGroupName, "任务组名称");

        JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);
        scheduler.resumeJob(jobKey);
    }

    /**
     * 删除触发器
     *
     * @param triggerName      触发器名称
     * @param triggerGroupName 触发器组名称
     * @throws SchedulerException 操作失败抛出异常
     */
    public static void deleteTrigger(String triggerName, String triggerGroupName) throws SchedulerException {
        Assert.hasLength(triggerName, "触发器名称");
        Assert.hasLength(triggerName, "触发器组名称");

        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
        scheduler.unscheduleJob(triggerKey);
    }

    /**
     * 暂停触发器,暂停期间任务将堆积
     *
     * @param triggerName      触发器名称
     * @param triggerGroupName 触发器组名称
     * @throws SchedulerException 操作失败抛出异常
     */
    public static void pauseTrigger(String triggerName, String triggerGroupName) throws SchedulerException {
        Assert.hasLength(triggerName, "触发器名称");
        Assert.hasLength(triggerName, "触发器组名称");

        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
        scheduler.pauseTrigger(triggerKey);
    }

    /**
     * 恢复触发器,暂停期间堆积的任务将立即执行
     *
     * @param triggerName      触发器名称
     * @param triggerGroupName 触发器组名称
     * @throws SchedulerException 操作失败抛出异常
     */
    public static void resumeTrigger(String triggerName, String triggerGroupName) throws SchedulerException {
        Assert.hasLength(triggerName, "触发器名称");
        Assert.hasLength(triggerName, "触发器组名称");

        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
        scheduler.resumeTrigger(triggerKey);
    }

    /**
     * Pause all triggers - similar to calling <code>pauseTriggerGroup(group)</code>
     * on every group, however, after using this method <code>resumeAll()</code>
     * must be called to clear the scheduler's state of 'remembering' that all
     * new triggers will be paused as they are added.
     *
     * <p>
     * When <code>resumeAll()</code> is called (to un-pause), trigger misfire
     * instructions WILL be applied.
     * </p>
     *
     * @see #resumeAll()
     * @see #standby()
     */
    public static void pauseAll() throws SchedulerException {
        scheduler.pauseAll();
    }

    /**
     * Resume (un-pause) all triggers - similar to calling
     * <code>resumeTriggerGroup(group)</code> on every group.
     *
     * <p>
     * If any <code>Trigger</code> missed one or more fire-times, then the
     * <code>Trigger</code>'s misfire instruction will be applied.
     * </p>
     *
     * @see #pauseAll()
     */
    public static void resumeAll() throws SchedulerException {
        scheduler.resumeAll();
    }

    /**
     * Starts the <code>Scheduler</code>'s threads that fire <code>{@link Trigger}s</code>.
     * When a scheduler is first created it is in "stand-by" mode, and will not
     * fire triggers.  The scheduler can also be put into stand-by mode by
     * calling the <code>standby()</code> method.
     *
     * <p>
     * The misfire/recovery process will be started, if it is the initial call
     * to this method on this scheduler instance.
     * </p>
     *
     * @throws SchedulerException if <code>shutdown()</code> has been called, or there is an
     *                            error within the <code>Scheduler</code>.
     * @see #standby()
     * @see #shutdown()
     */
    public static void start() throws SchedulerException {
        scheduler.start();
    }

    /**
     * Temporarily halts the <code>Scheduler</code>'s firing of <code>{@link Trigger}s</code>.
     *
     * <p>
     * When <code>start()</code> is called (to bring the scheduler out of
     * stand-by mode), trigger misfire instructions will NOT be applied
     * during the execution of the <code>start()</code> method - any misfires
     * will be detected immediately afterward (by the <code>JobStore</code>'s
     * normal process).
     * </p>
     *
     * <p>
     * The scheduler is not destroyed, and can be re-started at any time.
     * </p>
     *
     * @see #start()
     * @see #pauseAll()
     */
    public static void standby() throws SchedulerException {
        scheduler.standby();
    }

    /**
     * Halts the <code>Scheduler</code>'s firing of <code>{@link Trigger}s</code>,
     * and cleans up all resources associated with the Scheduler. Equivalent to
     * <code>shutdown(false)</code>.
     *
     * <p>
     * The scheduler cannot be re-started.
     * </p>
     *
     * @see #shutdown(boolean)
     */
    public static void shutdown() throws SchedulerException {
        scheduler.shutdown();
    }

    /**
     * Halts the <code>Scheduler</code>'s firing of <code>{@link Trigger}s</code>,
     * and cleans up all resources associated with the Scheduler.
     *
     * <p>
     * The scheduler cannot be re-started.
     * </p>
     *
     * @param waitForJobsToComplete if <code>true</code> the scheduler will not allow this method
     *                              to return until all currently executing jobs have completed.
     * @see #shutdown
     */
    public static void shutdown(boolean waitForJobsToComplete) throws SchedulerException {
        scheduler.shutdown(waitForJobsToComplete);
    }
}
